package net.wicp.tams.common.spring.autoconfig;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.collections.MapUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import net.wicp.tams.common.spring.autoconfig.constant.SpringConf;

public class SpringAssit {
	// 为了非spring程序调用spring程序
	public static ApplicationContext context;

	private final Environment environment;

	public SpringAssit(Environment environment) {
		this.environment = environment;
	}

	public Map<String, String> findAllProps() {
		Map<String, LinkedHashMap<String, String>> retlist = findAllPropsCollect();
		LinkedHashMap<String, String> allMap = new LinkedHashMap<>();
		// List<LinkedHashMap<String, String>> lastPut = new ArrayList<>();
		for (String ele : retlist.keySet()) {
			SpringConf springConfTemp = null;
			for (SpringConf springConf : SpringConf.values()) {
				if (ele.startsWith(springConf.getKeyPre())) {
					springConfTemp = springConf;
					break;
				}
			}
			if (springConfTemp != null) {
				springConfTemp.getProps().put(ele, retlist.get(ele));
			} else {
				LinkedHashMap<String, String> tempmap = retlist.get(ele);
				allMap.putAll(tempmap);
			}
		}
		for (SpringConf springConf : SpringConf.sortDesc()) {
			for (String ele : springConf.getProps().keySet()) {
				allMap.putAll(springConf.getPropsConvert(ele, allMap));
			}
		}
		return allMap;
	}

	/***
	 * 找到类型中有Annotation的域
	 * 
	 * @param targetType
	 * @param classA
	 * @return
	 */
	public static List<Field> selectFields(Class<?> targetType, final Class<? extends Annotation> classA) {
		Set<Class<?>> handlerTypes = new LinkedHashSet<>();
		Class<?> specificHandlerType = null;
		if (!Proxy.isProxyClass(targetType)) {
			specificHandlerType = ClassUtils.getUserClass(targetType);
			handlerTypes.add(specificHandlerType);
		}
		handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
		final List<Field> retlist = new ArrayList<>();
		for (Class<?> currentHandlerType : handlerTypes) {
			final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
			ReflectionUtils.doWithFields(targetClass, new ReflectionUtils.FieldCallback() {
				@Override
				public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
					Annotation declaredAnnotation = field.getDeclaredAnnotation(classA);
					if (declaredAnnotation != null) {
						retlist.add(field);
					}
				}
			});

		}
		return retlist;
	}

	/***
	 * 找到类中有相关注解的方法
	 * 
	 * @param handler
	 * @param classA
	 * @param applicationContext
	 * @return
	 */
	public static Set<Method> detectHandlerFiled(final Object handler, final Class<? extends Annotation> classA,
			ApplicationContext applicationContext) {
		Class<?> handlerType = (handler instanceof String) ? applicationContext.getType((String) handler)
				: handler.getClass();
		final Class<?> userType = ClassUtils.getUserClass(handlerType);

		Set<Method> methods = MethodIntrospector.selectMethods(userType, new ReflectionUtils.MethodFilter() {
			@Override
			public boolean matches(Method method) {// 只选择被@RequestMapping标记的方法
				Annotation findAnnotation = AnnotationUtils.findAnnotation(method, classA);
				return findAnnotation != null;
			}
		});
		return methods;
	}

	public Map<String, LinkedHashMap<String, String>> findAllPropsCollect() {
		Map<String, LinkedHashMap<String, String>> result = new LinkedHashMap<>();
		for (Entry<String, PropertySource<?>> entry : getPropertySources().entrySet()) {
			PropertySource<?> source = entry.getValue();
			String sourceName = entry.getKey();
			if (sourceName.startsWith("tams:")) {// 自已定义的属性去掉
				continue;
			}
			if (source instanceof EnumerablePropertySource) {
				EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source;
				LinkedHashMap<String, String> properties = new LinkedHashMap<String, String>();
				for (String name : enumerable.getPropertyNames()) {
					properties.put(name, String.valueOf(enumerable.getProperty(name)));
				}
				if (properties != null && MapUtils.isNotEmpty(properties)) {
					result.put(sourceName, properties);
				}
			}
		}
		return result;
	}

	private Map<String, PropertySource<?>> getPropertySources() {
		Map<String, PropertySource<?>> map = new LinkedHashMap<String, PropertySource<?>>();
		MutablePropertySources sources = null;
		if (environment != null && environment instanceof ConfigurableEnvironment) {
			sources = ((ConfigurableEnvironment) environment).getPropertySources();
		} else {
			sources = new StandardEnvironment().getPropertySources();
		}
		for (PropertySource<?> source : sources) {
			extract("", map, source);
		}
		return map;
	}

	private void extract(String root, Map<String, PropertySource<?>> map, PropertySource<?> source) {
		if (source instanceof CompositePropertySource) {
			for (PropertySource<?> nest : ((CompositePropertySource) source).getPropertySources()) {
				extract(source.getName() + ":", map, nest);
			}
		} else {
			map.put(root + source.getName(), source);
		}
	}
}
